/*
  Copyright 2006-2012 Patrik Jonsson, sunrise@familjenjonsson.org

  This file is part of Sunrise.

  Sunrise is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 3 of the License, or
  (at your option) any later version.

  Sunrise is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with Sunrise.  If not, see <http://www.gnu.org/licenses/>.
  
*/

/// \file
/// Contains the grid_cell class.

// (This file is included automatically by grid.h)

#include "blitz/numinquire.h"
#include "ray.h"
#include "hilbert.h"

#ifdef HAVE_BOOST_SERIALIZATION
#include <boost/serialization/serialization.hpp>
#endif

/** A cell in a hierarchichal grid. The grid_cell objects are
    contained in a grid represented by a grid_base object. A cell
    either contains a subgrid or, if it is a leaf cell, a T_data
    pointer to whatever is being contained in the cells. */
template<typename cell_data_type>
class mcrx::grid_cell {
public:
  typedef mcrx::T_float T_float;
  typedef octogrid<cell_data_type> T_grid;
  typedef grid_cell T_cell;
  typedef cell_data_type T_data;
  typedef unsigned int T_indextype;
  typedef blitz::TinyVector<T_indextype, 3> T_index;
  typedef cell_tracker<T_data> T_cell_tracker;

private:
  typedef int T_neighbor_offset;
  friend class blitz::TinyVector < T_cell, 8 >;
  friend class blitz::MemoryBlock<T_cell> ;
  friend class octogrid<cell_data_type> ;
  friend class adaptive_grid<cell_data_type> ;

protected:

  bool leaf; ///< Indicates whether cell is refined or not.

  /// The MPI task of this cell
  int task_;

  union {
    /// Pointer to a possible refined grid in this cell (mutually
    /// exclusive with celldata).
    T_grid* sub;
    /// Pointer to data for this cell (mutually exclusive with sub)
    T_data* celldata;
  };

  void placement_new_destructor ();

  /** Clears the celldata pointer. Used when the data objects were
      created using placement new in adaptive_grid. */
  void unset_data() {celldata=0;};

  // Neighbor search and ray/cell intersections
  T_cell_tracker neighbor (const vec3d& p,int dim, bool positive_dir) const;

public:
  grid_cell ();
  // creates a cell on the specified task.
  grid_cell(int task);
  ~grid_cell();

  /// Copy constructor does NOT copy sub grids or cell data!
  grid_cell (const T_cell&);

  T_cell& operator= (const T_cell&);

  /// Returns the pointer to the cell data object.
  T_data* data() const {assert (leaf); return celldata;};
  /** Sets the celldata pointer. This is only permissible if a data
      object is not already set. */
  void set_data(T_data* d) {assert (leaf); assert(!celldata);
    //if (celldata) delete celldata;
    assert (d); celldata = d;};

  /** Sets the celldata pointer, without deleting the object
      previously pointed to. This is used when the current celldata
      was created using placement (this is not optimal). */
  void set_data_nodelete(T_data* d) {assert (leaf); celldata = d;};

  /** Finds the fractional distance along a ray that corresponds to a
      certain column density through the cell. Since the cells are
      uniform density, this is trivial for this grid. */
  T_float column_to_distance_fraction(/// The column density fraction you want distance for.
				      T_float fn,
				      /// The starting position
				      const vec3d& pos, 
				      /// The direction along which the column is calculated
				      const vec3d& dir,
				      /// Total length of the segment
				      T_float dl) const {
    return fn; };

  // refinement
  void refine ();
  void refine (void*&);
  void unrefine (T_data*const);
  const T_grid* sub_grid () const {assert (!leaf); assert (sub); return sub;};
  T_grid* sub_grid () {assert (!leaf); assert (sub); return sub;};

  /// Returns whether the cell is a leaf cell or not.
  bool is_leaf () const {return leaf;};

  /// Returns the task of this cell.
  int task() const { assert(is_leaf()); return task_; };
};

// *** constructors/destructors ***

/// Default constructor is just for the array allocations.
template < typename cell_data_type >
mcrx::grid_cell<cell_data_type>::grid_cell():
  leaf (true), task_(-1), celldata (0)
{
}

template < typename cell_data_type >
mcrx::grid_cell<cell_data_type>::grid_cell(int task):
  leaf (true), task_(task), celldata (0)
{
}

/** Copy constructor only copies the task, not the subgrids or
    cell data. */
template < typename cell_data_type >
mcrx::grid_cell<cell_data_type>::grid_cell(const T_cell& c):
  leaf (true), task_(c.task_), celldata (0)
{
}

template < typename cell_data_type >
mcrx::grid_cell<cell_data_type> ::~grid_cell()
{
  if (is_leaf ()) {
    delete celldata;
  } 
  else
    delete sub;
}

/** "Destructor" when placement new is used. The idea here is that we
    want to preempt the actual destructor from deleting sub if we have
    used placement new, so the top-level (adaptive_grid) destructor
    checks for that and calls this function first if that is the
    case. */
template < typename cell_data_type >
void mcrx::grid_cell<cell_data_type>::placement_new_destructor ()
{
  if (!is_leaf ()) {
    assert (sub);
    sub->placement_new_destructor();
    sub->~T_grid();
    sub = 0;
  }
}

// *** grid_cell methods ***

template<typename cell_data_type>
mcrx::grid_cell<cell_data_type>&
mcrx::grid_cell<cell_data_type>::operator= (const T_cell& rhs) 
{
  if (this == &rhs)
    return *this;

  // assignment operator does NOT copy sub grids/cell data, so it
  // should never be used for this.  Basically it's only here because
  // the TinyVector constructor (in octogrid) needs it.  (This sucks
  // because it opens a gaping hole in the management of the sub
  // grid/cell data pointers.)
  assert (rhs.sub == 0);
  
  leaf = rhs.leaf;
  task_ = rhs.task_;
  assert (!sub);
  sub = rhs.sub; assert(!rhs.sub);

  return (*this);
}


/** Refines a grid_cell to contain a sub-grid. */
template < typename cell_data_type >
void mcrx::grid_cell<cell_data_type>::refine () 
{
  assert (is_leaf ());
  if (celldata)
    delete celldata;
  
  sub = octogrid<T_data>::create_sub(task());
  leaf = false;
}

/** "Placement new" version of refine. Refines a grid_cell and puts
    the sub grid in the specified location in memory. */
template < typename cell_data_type >
void mcrx::grid_cell<cell_data_type>::refine (void*& placement) 
{
  assert (is_leaf ());
  if (celldata)
    delete celldata;

  sub = octogrid<T_data>::create_sub(task(), placement);
  leaf = false;
}

/** "Unrefines" a cell and replaces it with the cell data object d. */
template < typename cell_data_type >
void mcrx::grid_cell<cell_data_type>::unrefine (T_data* const d) 
{ 
  assert(!is_leaf());
  delete sub_grid ();
  leaf = true;
  celldata = d;
}
